package com.kujie.common.modbus;

import com.serotonin.modbus4j.ModbusFactory;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.exception.ErrorResponseException;
import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.serotonin.modbus4j.ip.IpParameters;
import com.serotonin.modbus4j.locator.BaseLocator;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ConcurrentHashMap;

/**
 * modbus通讯工具类,采用modbus4j实现
 *
 * @author jyy
 * @date 2025-02-07
 */
@Slf4j
public class ModbusUtils {

    static ModbusFactory modbusFactory;

    private static final ConcurrentHashMap<String, ModbusMaster> MASTER_MAP = new ConcurrentHashMap<>();

    static {
        modbusFactory = new ModbusFactory();
    }

    /**
     * 获取master
     *
     * @param host 主机ip
     * @param port 端口
     * @return ModbusMaster
     */
    public static ModbusMaster getMaster(String host, int port) {

        String key = host + port;
        if (MASTER_MAP.containsKey(key)) {
            return MASTER_MAP.get(key);
        }

        IpParameters params = new IpParameters();
        params.setHost(host);
        params.setPort(port);
        // TCP 协议
        ModbusMaster master = modbusFactory.createTcpMaster(params, true);
        try {
            //设置超时时间
            master.setTimeout(3000);
            //设置重连次数
            master.setRetries(3);
            //初始化
            master.init();

            MASTER_MAP.put(key, master);
        } catch (ModbusInitException e) {
            e.printStackTrace();
        }
        return master;
    }

    /**
     * 读取[01 Coil Status 0x]类型 开关数据
     *
     * @param slaveId slaveId
     * @param offset  位置
     * @return 读取值
     * @throws ModbusTransportException 异常
     * @throws ErrorResponseException   异常
     */
    public static Boolean readCoilStatus(String host, int port, int slaveId, int offset)
        throws ModbusTransportException, ErrorResponseException {
        // 01 Coil Status
        BaseLocator<Boolean> loc = BaseLocator.coilStatus(slaveId, offset);
        Boolean value = getMaster(host, port).getValue(loc);
        return value;
    }

    /**
     * 读取[02 Input Status 1x]类型 开关数据
     *
     * @param slaveId slaveId
     * @param offset  偏移量
     * @return Boolean
     * @throws ModbusTransportException
     * @throws ErrorResponseException
     */
    public static Boolean readInputStatus(String host, int port, int slaveId, int offset)
        throws ModbusTransportException, ErrorResponseException {
        // 02 Input Status
        BaseLocator<Boolean> loc = BaseLocator.inputStatus(slaveId, offset);
        Boolean value = getMaster(host, port).getValue(loc);
        return value;
    }

    /**
     * 读取[03 Holding Register类型 2x]模拟量数据
     *
     * @param slaveId  slave Id
     * @param offset   位置
     * @param dataType 数据类型,来自com.serotonin.modbus4j.code.DataType
     * @return Number
     * @throws ModbusTransportException 异常
     * @throws ErrorResponseException   异常
     */
    public static Number readHoldingRegister(String host, int port, int slaveId, int offset, int dataType)
        throws ModbusTransportException, ErrorResponseException {
        // 03 Holding Register类型数据读取
        BaseLocator<Number> loc = BaseLocator.holdingRegister(slaveId, offset, dataType);
        Number value = getMaster(host, port).getValue(loc);
        return value;
    }

    /**
     * 读取[04 Input Registers 3x]类型 模拟量数据
     *
     * @param slaveId  slaveId
     * @param offset   位置
     * @param dataType 数据类型,来自com.serotonin.modbus4j.code.DataType
     * @return 返回结果
     * @throws ModbusTransportException 异常
     * @throws ErrorResponseException   异常
     */
    public static Number readInputRegisters(String host, int port, int slaveId, int offset, int dataType)
        throws ModbusTransportException, ErrorResponseException {
        // 04 Input Registers类型数据读取
        BaseLocator<Number> loc = BaseLocator.inputRegister(slaveId, offset, dataType);
        Number value = getMaster(host, port).getValue(loc);
        return value;
    }

    /**
     * 批量读取使用方法
     */
//    public static List<Object> batchRead(String host, int port, int slaveId, List<Record> cfgs) throws ModbusTransportException, ErrorResponseException {
//        List<Object> objs = new ArrayList<>();
//        BatchRead<String> batchRead = new BatchRead<>();
//
//        for (Record cfg : cfgs) {
//            String id = cfg.getStr("ID");
//            int registerRang = cfg.getInt("MODBUS_CMD");
//            int offset = cfg.getInt("MODBUS_ADDR");
//            switch (registerRang) {
//                case RegisterRange.COIL_STATUS:
//                    batchRead.addLocator(id, BaseLocator.coilStatus(slaveId, offset));
//                    break;
//                case RegisterRange.INPUT_STATUS:
//                    batchRead.addLocator(id, BaseLocator.inputStatus(slaveId, offset));
//                    break;
//                case RegisterRange.HOLDING_REGISTER:
//                    batchRead.addLocator(id, BaseLocator.holdingRegister(slaveId, offset, DataType.FOUR_BYTE_FLOAT));
//                    break;
//                case RegisterRange.INPUT_REGISTER:
//                    batchRead.addLocator(id, BaseLocator.inputRegister(slaveId, offset, DataType.EIGHT_BYTE_INT_UNSIGNED));
//                    break;
//                default:
//                    break;
//            }
//        }
//
//        BatchResults<String> results = getMaster(host, port).send(batchRead);
//        System.out.println(results);
//        for (Record cfg : cfgs) {
//            String id = cfg.getStr("ID");
//            objs.add(results.getValue(id));
//        }
//        return objs;
//    }


    /**
     * 测试
     *
     * @param args
     */
//    public static void main(String[] args) {
//        try {
//            String host = "192.168.2.233";
//            int port = 502;
//            int slaveId = 1;
//            // 01测试
////            Boolean v011 = readCoilStatus(host, port, slaveId, 1);
////            System.out.println("---v011:" + v011);
//
//            // 03测试
////            Number v031 = readHoldingRegister(host, port, slaveId, 1, DataType.FOUR_BYTE_FLOAT);
////            System.out.println("---v032:" + v031);
//
//            // 批量读取
//            List<Record> records = new ArrayList<>();
//            Record record = new Record();
//            record.set("ID", "1");
//            record.set("MODBUS_CMD", 1);
//            record.set("MODBUS_ADDR", 1);
//            records.add(record);
//
//            record = new Record();
//            record.set("ID", "2");
//            record.set("MODBUS_CMD", 1);
//            record.set("MODBUS_ADDR", 2);
//            records.add(record);
//
//            record = new Record();
//            record.set("ID", "3");
//            record.set("MODBUS_CMD", 3);
//            record.set("MODBUS_ADDR", 0);
//            records.add(record);
//
//            record = new Record();
//            record.set("ID", "4");
//            record.set("MODBUS_CMD", 3);
//            record.set("MODBUS_ADDR", 2);
//            records.add(record);
//
//            record = new Record();
//            record.set("ID", "5");
//            record.set("MODBUS_CMD", 3);
//            record.set("MODBUS_ADDR", 4);
//            records.add(record);
//            record = new Record();
//            record.set("ID", "6");
//            record.set("MODBUS_CMD", 4);
//            record.set("MODBUS_ADDR", 0);
//            records.add(record);
//
//            List<Object> objs = batchRead(host, port, 1, records);
//
//            for (Object obj : objs) {
//                if (obj instanceof Boolean) {
//                    System.out.println("boolean\t" + obj);
//                } else if (obj instanceof Float){
//                    System.out.println("float\t" + obj);
//                }
//            }
//
//            objs = batchRead("192.168.2.26", port, 1, records);
//            for (Object obj : objs) {
//                if (obj instanceof Boolean) {
//                    System.out.println("boolean\t" + obj);
//                } else if (obj instanceof Float){
//                    System.out.println("float\t" + obj);
//                }
//            }
//
//            for (int i = 0; i < 10; i++) {
//                Thread.sleep(5000);
//                objs = batchRead(host, port, 1, records);
//
//                for (Object obj : objs) {
//                    if (obj instanceof Boolean) {
//                        System.out.println("boolean\t" + obj);
//                    } else if (obj instanceof Float){
//                        System.out.println("float\t" + obj);
//                    }
//                }
//
//                objs = batchRead("192.168.2.26", port, 1, records);
//                for (Object obj : objs) {
//                    if (obj instanceof Boolean) {
//                        System.out.println("boolean\t" + obj);
//                    } else if (obj instanceof Float){
//                        System.out.println("float\t" + obj);
//                    }
//                }
//            }
//
//        } catch (Exception e) {
//            log.error(e.toString());
//        }
//    }
}
